import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class UDPClient {
    private static final int TIMEOUT = 100; // 超时时间，单位毫秒
    private static final int RETRIES = 2; // 每个请求的重传次数
    private static final byte VERSION = 2; // 版本号
    private static final int MAX_PACKET_SIZE = 203; // 最大数据包大小
    private static final int CLIENT_PORT = 666; // 客户端固定端口号

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("请注意格式: java UDPClient <serverIP> <serverPort>");
            return;
        }

        String serverIP = args[0];
        int serverPort = Integer.parseInt(args[1]);

        try (DatagramSocket clientSocket = new DatagramSocket(CLIENT_PORT)) {
            clientSocket.setSoTimeout(TIMEOUT);

            InetAddress serverAddress = InetAddress.getByName(serverIP);
            byte[] receiveData = new byte[MAX_PACKET_SIZE];

            int receivedPackets = 0;
            long totalRTT = 0;
            long minRTT = Long.MAX_VALUE;
            long maxRTT = Long.MIN_VALUE;
            long firstResponseTime = 0;
            long lastResponseTime = 0;
            List<Long> rttList = new ArrayList<>();

            for (int sequenceNumber = 1; sequenceNumber <= 12; sequenceNumber++) {
                boolean receivedResponse = false;

                for (int attempt = 0; attempt <= RETRIES; attempt++) {
                    byte[] sendData = createPacket(sequenceNumber);
                    DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);

                    long sendTime = System.currentTimeMillis();
                    clientSocket.send(sendPacket);

                    try {
                        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                        clientSocket.receive(receivePacket);

                        long receiveTime = System.currentTimeMillis();
                        long rtt = receiveTime - sendTime;

                        int receivedSeqNo = ((receiveData[0] & 0xFF) << 8) | (receiveData[1] & 0xFF);
                        byte receivedVersion = receiveData[2];

                        if (receivedSeqNo == sequenceNumber && receivedVersion == VERSION) {
                            receivedPackets++;
                            totalRTT += rtt;
                            minRTT = Math.min(minRTT, rtt);
                            maxRTT = Math.max(maxRTT, rtt);
                            rttList.add(rtt);
                            String serverTime = new String(receiveData, 3, 8, StandardCharsets.UTF_8).trim();

                            if (firstResponseTime == 0) {
                                firstResponseTime = receiveTime;
                            }
                            lastResponseTime = receiveTime;

                            System.out.println(String.format("序列号：%d，服务器：%s:%d，RTT：%.3f ms，服务器时间：%s", sequenceNumber, serverIP, serverPort, (double) rtt, serverTime));
                            receivedResponse = true;
                            break;
                        }
                    } catch (SocketTimeoutException e) {
                        System.out.println("序列号：" + sequenceNumber + "，请求超时");
                    }
                }

                if (!receivedResponse) {
                    System.out.println("序列号：" + sequenceNumber + "，两次重传失败，放弃此次重传");
                }
            }

            // 打印汇总信息
            double lossRate = 1 - (double) receivedPackets / 12;
            double avgRTT = receivedPackets > 0 ? (double) totalRTT / receivedPackets : 0;
            long responseTimeDiff = lastResponseTime - firstResponseTime;
            double stdDevRTT = calculateStdDev(rttList, avgRTT);

            System.out.println("【汇总】");
            System.out.println(String.format("接收到的UDP数据包数：%d", receivedPackets));
            System.out.println(String.format("丢包率：%.3f%%", lossRate * 100));
            System.out.println(String.format("最大RTT：%.3f ms", (double) maxRTT));
            System.out.println(String.format("最小RTT：%.3f ms", (double) minRTT));
            System.out.println(String.format("平均RTT：%.3f ms", avgRTT));
            System.out.println(String.format("RTT标准差：%.3f ms", stdDevRTT));
            System.out.println(String.format("服务器整体响应时间：%d ms", responseTimeDiff));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static byte[] createPacket(int sequenceNumber) {
        int fixedFieldsLength = 2 + 1; // 序列号和版本号的长度
        int paddingLength = MAX_PACKET_SIZE - fixedFieldsLength;
        byte[] packet = new byte[MAX_PACKET_SIZE];
        packet[0] = (byte) (sequenceNumber >> 8); // 高8位
        packet[1] = (byte) (sequenceNumber & 0xFF); // 低8位
        packet[2] = VERSION; // 版本号
        for (int i = 3; i < paddingLength; i++) {
            packet[i] = (byte) 0x00; // 填充数字 '0'
        }
        return packet;
    }

    private static double calculateStdDev(List<Long> rttList, double mean) {
        if (rttList.isEmpty()) {
            return 0.0;
        }
        double sum = 0;
        for (long rtt : rttList) {
            sum += Math.pow(rtt - mean, 2);
        }
        return Math.sqrt(sum / rttList.size());
    }
}
